/**
 * Copyright (c) 2022 Beijing Jiaotong University
 * PhotLab is licensed under [Open Source License].
 * You can use this software according to the terms and conditions of the [Open Source License].
 * You may obtain a copy of [Open Source License] at: [https://open.source.license/]
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 *
 * See the [Open Source License] for more details.
 *
 * Author: Qing'ao Zhang, Zhenjie Wei
 * Created: Mar. 15, 2023
 * Supported by: National Key Research and Development Program of China
 */

import {
	PhotComponent,
	RenderWayPoint,
	PhotParameter,
	RenderRoute,
	RenderRect,
	RenderGraphNode,
	RenderTextArea,
	RenderGraph,
	PhotGraphLink,
	PhotGraph
} from "../thrift/gen-nodejs/graph_data_type_types"
import thriftClient from "../utils/conn"
import service from "../thrift/gen-nodejs/Graph"

export default class GraphService {
	private _client: any
	constructor(socket: [string, number]) {
		this._client = thriftClient(service, socket[0], socket[1])
	}

	/**
	 * Open a computational graph, load the data of the computational graph, and generate a computational graph model 
	 * (PhotGraph) and a computational graph view (RenderGraph).
	 * @param graphPath - Path of the computational graph.
	 * @returns The PhotGraph.
	 */
	public async openGraph(graphPath: string): Promise<PhotGraph> {
		return await this._client.open_graph(graphPath)
	}

	/**
	 * According to the given calculation graph ID, save the calculation graph into the file 
	 * that loads this calculation graph.
	 * @param graphId - The ID of the computation graph that needs to be saved.
	 * @returns null
	 */
	public async saveGraph(graphId: string) {
		return await this._client.save_graph(graphId)
	}

	/**
	 * According to the given calculation graph ID and the specified path, save the specified calculation graph
	 * as another file.
	 * @param graphPath - The path to save the computation graph.
	 * @param graphId - Saved computation graph ID.
	 * @returns null.
	 */
	public async saveGraphAs(graphPath: string, graphId: string) {
		return await this._client.save_graph_as(graphPath, graphId)
	}

	/**
	 * Create a new computational graph model (PhotGraph) and a new computational graph view (RenderGraph)
	 * based on the given calculation graph ID.
	 * @param graphId - New calculation graph ID.
	 * @returns Computational graph model:PhotGraph.
	 */
	public async newGraph(graphId: string): Promise<PhotGraph> {
		return await this._client.new_graph(graphId)
	}

	/**
	 * Obtain the computational graph model.
	 * @param graphId - The graph id.
	 * @returns The whole graph find by 'name'.
	 */
	public async getGraph(graphId: string): Promise<PhotGraph> {
		return await this._client.get_graph(graphId)
	}

	/**
	 * Get the computational graph view.
	 * @param graphId - The graph id.
	 * @returns The computational graph view:RenderGraph.
	 */
	public async getRenderGraph(graphId: string): Promise<RenderGraph> {
		return await this._client.get_render_graph(graphId)
	}

	public async alterGraphAttribute(graphId: string, attributeName: string, attributeValue: string) {
		return await this._client.alter_graph_attribute(graphId, attributeName, attributeValue)
	}

	/**
	 * According to the given calculation graph ID, close the calculation graph. The server will delete 
	 * the corresponding calculation graph model and calculation graph view.
	 * @param graphId - The graph id. 
	 * @param ignoreChange - Whether to ignore modifications to the computation graph.
	 * @returns 
	 */
	public async closeGraph(graphId: string, ignoreChange = false) {
		return await this._client.close_graph(graphId, ignoreChange)
	}
	
	/**
	 * Close all opened graph. 
	 * @param save - save all opened graph. 
	 * @returns 
	 */
	public async closeAllOpenedGraph(save = true) {
		return await this._client.close_all_opened_graph(save)
	}

	/**
	 * Rename the corresponding computational graph model and computational graph view.
	 * @param graphId - Old graph id.
	 * @param newName - New graph id.
	 * @returns null.
	 */
	public async renameGraph(graphId: string, newName: string) {
		return await this._client.rename_graph(graphId, newName)
	}

	/**
	 * Get all opened computation graphs.
	 * @returns Return a dictionary containing <graphId, graphName>.
	 */
	public async getOpenedGraphList(): Promise<Map<string, string>> {
		return await this._client.get_opened_graph_list()
	}

	/**
	 * Get all the parameters of the computational graph.
	 * @param graphId - The graph id.
	 * @returns Graph parameter list.
	 */
	public async getGraphParameters(graphId: string): Promise<PhotParameter[]> {
		return await this._client.get_graph_parameters(graphId)
	}

	/**
	 * Get a computation graph parameter.
	 * @param graphId - The graph name.
	 * @param graphParameterName - Parameter Name.
	 * @returns PhotParameter: Calculating graph variables.
	 */
	public async getGraphParameter(graphId: string, graphParameterName: string): Promise<PhotParameter> {
		return await this._client.get_graph_parameter(graphId, graphParameterName)
	}

	/**
	 * Alter a graph parameter in specified graph.
	 * @param graphId - The graph id.
	 * @param graphParameterName - The graph parameter.
	 * @param graphParameter - PhotParameter object that contain parameter data.
	 * @returns null.
	 */
	public async alterGraphParameter(
		graphId: string,
		graphParameterName: string,
		graphParameter: PhotParameter
	) {
		return await this._client.alter_graph_parameter(graphId, graphParameterName, graphParameter)
	}

	/**
	 * Save all graph parameters in specified graph.
	 * @param graphId - The graph id.
	 * @param graphParameters - Graph parameter list.
	 * @returns null.
	 */
	public async alterGraphParameters(
		graphId: string,
		graphParameters: Map<string, PhotParameter>
	) {
		return await this._client.alter_graph_parameters(graphId, graphParameters)
	}

	/**
	 * Add a graph parameter in specified graph.
	 * @param graphId - The graph id.
	 * @param graphParameter - The graph parameter.
	 * @returns PhotParameter: Calculating graph variables.
	 */
	public async addGraphParameter(graphId: string, graphParameter: PhotParameter) {
		return await this._client.add_graph_parameter(graphId, graphParameter)
	}

	/**
	 * Delete a graph parameter in specified graph.
	 * @param graphId - The graph id.
	 * @param graphParameterName - The graph parameter name.
	 * @returns null.
	 */
	public async deleteGraphParameter(graphId: string, graphParameterName: string) {
		return await this._client.delete_graph_parameter(graphId, graphParameterName)
	}

	/**
	 * Get a specified component in specified graph.
	 * @param graphId - The graph id.
	 * @param componentId - The component id.
	 * @returns The specified component.
	 */
	public async getComponent(graphId: string, componentId: number): Promise<PhotComponent> {
		return await this._client.get_component(graphId, componentId)
	}

	/**
	 * Get all components in specified graph.
	 * @param graphId - The graph id.
	 * @returns list<PhotComponent>: List of component model objects.
	 */
	public async getComponents(graphId: string): Promise<PhotComponent[]> {
		return await this._client.get_components(graphId)
	}

	/**
	 * Add a component model object to the computation graph..
	 * @param graphId - The graph id
	 * @param component - (PhotComponent): Component model object.
	 * @returns (PhotComponent): Component model object.
	 */
	public async addComponent(graphId: string, component: PhotComponent): Promise<PhotComponent[]> {
		return await this._client.add_component(graphId, component)
	}

	/**
	 * Select a component from the component library and add it to the specified position in the computation graph, 
	 * simultaneously adding the component to both the computational graph model (PhotGraph) and 
	 * the computational graph view (RenderGraph)..
	 * @param graphId - The graph id.
	 * @param componentPath - The path of the component in the component library.
	 * @param componentId - The componentId.
	 * @param rect - A rectangle that describes the position and size of a component.
	 * @returns (PhotComponent): Component model object.
	 */
	public async addComponentFromLibrary(
		graphId: string,
		componentName: string,
		rect: RenderRect
	): Promise<PhotComponent> {
		return await this._client.add_component_from_library(graphId, componentName, rect)
	}

	/**
	 * Delete a component model object from the computational graph model.
	 * 
	 * If cascade_delete=True: both the component model objects of the computational graph model (PhotGraph) and 
	 * the component rendering objects of the render graph view (RenderGraph) will be deleted simultaneously. 
	 * In addition, the connection model objects (PhotGraphLink) and connection rendering objects (RenderRoute) 
	 * related to the components will also be deleted.
	 * If cascade_delete=False: only the component model objects of the computational graph model (PhotGraph) will be deleted.
	 * By default, cascade_delete=False.
	 * @param graphId - The graph id.
	 * @param componentId - Component id of the component to be deleted.
	 * @param cascadeDelete - Whether to delete the flag of the component view object.
	 * @returns The deleted component response.
	 */
	public async deleteComponent(graphId: string, componentId: number, cascadeDelete = false) {
		return await this._client.delete_component(graphId, componentId, cascadeDelete)
	}

	/**
	 * Modify a component model object.
	 * @param graphId - The graph id.
	 * @param component - (PhotComponent): Component model object.
	 * @returns null.
	 */
	public async alterComponent(
		graphId: string,
		component: PhotComponent
	) {
		return await this._client.alter_component(graphId, component)
	}

	/**
	 * Get a component rendering object.
	 * @param graphId - The graph id.
	 * @param componentId - The component id.
	 * @returns RenderGraphNode: Component rendering object.
	 */
	public async getRenderNode(graphId: string, componentId: number): Promise<RenderGraphNode> {
		return await this._client.get_render_node(graphId, componentId)
	}

	/**
	 * Get all the rendered objects of components in the computational graph view.
	 * @param graphId - The graph id.
	 * @returns list<RenderGraphNode>: Component rendering object list.
	 */
	public async getRenderNodes(graphId: string): Promise<RenderGraphNode[]> {
		return await this._client.get_render_nodes(graphId)
	}

	/**
	 * Add a component rendering object to the computational graph view.
	 * @param graphId - The graph id.
	 * @param node - (RenderGraphNode): Component rendering object.
	 * @returns The add link response.
	 */
	public async addRenderNode(graphId: string, node: RenderGraphNode): Promise<RenderGraphNode> {
		return await this._client.add_render_node(graphId, node)
	}

	/**
	 * Delete the rendering object of a component in the calculation graph view.
	 * 
	 * If cascade_delete=True: both the component model objects of the computational graph model (PhotGraph) and 
	 * the component rendering objects of the render graph view (RenderGraph) will be deleted simultaneously. 
	 * In addition, the connection model objects (PhotGraphLink) and connection rendering objects (RenderRoute) 
	 * related to the components will also be deleted.
	 * 
	 * If cascade_delete=False: only the component rendering objects of the algorithmic graph view (RenderGraph) 
	 * will be deleted.
	 * The default value for cascade_delete is False.
	 * 
	 * @param graphId - The graph id.
	 * @param componentId - The component id.
	 * @param cascadeDelete - The deleteModel.
	 * @returns The response of delete render route.
	 */
	public async deleteRenderNode(graphId: string, componentId: number, cascadeDelete = false) {
		return await this._client.delete_render_node(graphId, componentId, cascadeDelete)
	}

	/**
	 * Modify the position and size data of a component rendering object.
	 * @param graphId - The graph id.
	 * @param componentId - The component id.
	 * @param rect - A rectangle that describes the position and size of a component.
	 * @returns null.
	 */
	public async setRenderNodeRect(
		graphId: string,
		componentId: number,
		rect: RenderRect
	) {
		return await this._client.set_render_node_rect(graphId, componentId, rect)
	}

	/**
	 * Modify the position and size data of a component rendering object.
	 * @param graphId - The graph id.
	 * @param componentId - The component id.
	 * @param rect - A rectangle that describes the position and size of a component.
	 * @returns null.
	 */
	public async RenderNodeMoveTo(
		graphId: string,
		componentId: number,
		x: number,
		y: number
	) {
		return await this._client.render_node_move_to(graphId, componentId, x, y)
	}

	/**
	 * Modify a component rendering object.
	 * @param graphId - The graph id.
	 * @param node - (RenderGraphNode): Component rendering object.
	 * @returns null.
	 */
	public async alterRenderNode(
		graphId: string,
		node: RenderGraphNode
	) {
		return await this._client.alter_render_node(graphId, node)
	}

	/**
	 * Add a connection model object.
	 * @param graphId - The graph id.
	 * @param link - Connect model objects.
	 * @returns Connect model objects:PhotGraphLink.
	 */
	public async addLink(graphId: string, link: PhotGraphLink): Promise<PhotGraphLink> {
		return await this._client.add_link(graphId, link)
	}

	/**
	 * Get all connected model objects.
	 * @param graphId - The graph id.
	 * @returns list<PhotGraphLink>:List of connected models.
	 */
	public async getLinks(graphId: string): Promise<PhotGraphLink[]> {
		return await this._client.get_links(graphId)
	}

	/**
	 * Get a connection model object.
	 * @param graphId - The graph id.
	 * @param linkId - The linkId.
	 * @returns PhotGraphLink: Connect model objects.
	 */
	public async getLink(graphId: string, linkId: number): Promise<PhotGraphLink> {
		return await this._client.get_link(graphId, linkId)
	}

	/**
	 * Delete a connected model object.
	 * 
	 * If cascade_delete=True: then both the connected model object (PhotGraphLink) and the connected 
	 * rendering object (RenderRoute) will be deleted simultaneously.
	 * 
	 * If cascade_delete=False: only the linked model object (PhotGraphLink) will be deleted.
	 * By default, cascade_delete=False.
	 * 
	 * @param graphId - The graph id.
	 * @param linkId - The linkId.
	 * @param deleteRender - Whether to delete the flag for connecting rendering objects.
	 * @returns null.
	 */
	public async deleteLink(graphId: string, linkId: number, deleteRender = false) {
		return await this._client.delete_link(graphId, linkId, deleteRender)
	}

	/**
	 * Modify a connected model object.
	 * @param graphId - The graph id.
	 * @param link - Connect model objects:PhotGraphLink.
	 * @returns The response of alter link.
	 */
	public async alterLink(graphId: string, link: PhotGraphLink) {
		return await this._client.alter_link(graphId, link)
	}

	/**
	 * Add a link to render the object.
	 * @param graphId - The graph id.
	 * @param route - Connect rendering objects:RenderRoute.
	 * @returns RenderRoute: Connect model objects.
	 */
	public async addRenderRoute(graphId: string, route: RenderRoute): Promise<RenderRoute> {
		return await this._client.add_render_route(graphId, route)
	}

	/**
	 * Get a connection rendering object.
	 * @param graphId - The graph id.
	 * @param linkId - The link id.
	 * @returns  RenderRoute: Connect model objects.
	 */
	public async getRenderRoute(graphId: string, linkId: number): Promise<RenderRoute> {
		return await this._client.get_render_route(graphId, linkId)
	}

	/**
	 * Delete a connected rendering object.
	 * 
	 * If cascade_delete=True: then both the connected model object (PhotGraphLink) and the connected rendering object
	 *  (RenderRoute) will be deleted simultaneously.
	 * If cascade_delete=False: only the connected rendering object (RenderRoute) will be deleted.
	 * By default, cascade_delete=False.
	 * 
	 * @param graphId - The graph id.
	 * @param linkId - The linkId.
	 * @param deleteModel - Whether to delete the connected model object.
	 * @returns null.
	 */
	public async deleteRenderRoute(graphId: string, linkId: number, deleteModel = false) {
		return await this._client.delete_render_route(graphId, linkId, deleteModel)
	}

	/**
	 * Set a midpoint in the path that connects the rendering objects.
	 * @param graphId - The graph id.
	 * @param linkId - The link id.
	 * @param points - list<RenderWayPoint>:Connecting the intermediate node list of rendering objects.
	 * @returns null.
	 */
	public async setRenderRoutePath(
		graphId: string,
		linkId: number,
		points: RenderWayPoint[]
	) {
		return await this._client.set_render_route_path(graphId, linkId, points)
	}

	/**
	 * Add a text area.
	 * @param graphId - The graph id.
	 * @param textarea - RenderTextArea: Text Area Object.
	 * @returns RenderTextArea: Text Area Object.
	 */
	public async addTextArea(graphId: string, textarea: RenderTextArea): Promise<RenderTextArea> {
		return await this._client.add_text_area(graphId, textarea)
	}

	/**
	 * Get a text area.
	 * @param graphId - The graph id.
	 * @param textId - The text id.
	 * @returns RenderTextArea: Text Area Object.
	 */
	public async getTextArea(graphId: string, textId: number): Promise<RenderTextArea> {
		return await this._client.get_text_area(graphId, textId)
	}

	/**
	 * Delete a text area.
	 * @param graphId - The graph id.
	 * @param textId - The text area Id to be deleted.
	 * @returns null.
	 */
	public async deleteTextArea(graphId: string, textId: number) {
		return await this._client.delete_text_area(graphId, textId)
	}

	/**
	 * Modify a text area.
	 * @param graphId - The graph id.
	 * @param textarea - RenderTextArea: Text Area Object.
	 * @returns null.
	 */
	public async alterTextArea(graphId: string, textarea: RenderTextArea) {
		return await this._client.alter_text_area(graphId, textarea)
	}

	/**
	 * Set the text content of a text area.
	 * @param graphId - The graph id.
	 * @param textId - Text area ID.
	 * @param text - The text.
	 * @returns null.
	 */
	public async setText(
		graphId: string,
		textId: number,
		text: string
	) {
		return await this._client.set_text(graphId, textId, text)
	}

	/**
	 * Set the position and size of a text area.
	 * @param graphId - The graph id.
	 * @param textId - Text area ID.
	 * @param rect - RenderRect: A rectangle that describes the position and size of the text area.
	 * @returns null.
	 */
	public async setTextAreaRect(
		graphId: string,
		textId: number,
		rect: RenderRect
	) {
		return await this._client.set_text_area_rect(graphId, textId, rect)
	}
}